#pragma once

#include "RdcTransport.h"
#include "ActionsQueuer.h"

#include <memory>
#include <boost\noncopyable.hpp>

namespace rdc_transport
{
    template<class ChannelCore, class Reader, class Writer>
    class VcRequirements
    {
    public:
        static bool CheckRequerements()
        {
            void (Reader::*testSetReadHandler)(IReadHandler*) = &Reader::SetReadHandler;
            testSetReadHandler;

            void (Writer::*testWrite)(Buffer*) = &Writer::Write;
            testWrite;

            void (Writer::*testSetWriter)(IVirtChannel*) = &Writer::SetInternalWriter;
            testSetWriter;

            return true;
        }
    };

    //Reader - Reading strategy
    //Writer - Writing strategy
    //ChannelCore - Resource owner.

    template<class ChannelCore, class Reader, class Writer>
    class CVirtualChannel 
        : private boost::noncopyable
        , public IVirtChannel
        , public IActionCallback<Buffer>
        , public IReadHandler
    {
    public:
        typedef std::auto_ptr<ChannelCore> pChannelCore;
        typedef std::auto_ptr<Reader> pReader;
        typedef std::auto_ptr<Writer> pWriter;

    public:
        CVirtualChannel(IReadHandler* readCb, pChannelCore channel, pReader reader, pWriter writer);
        ~CVirtualChannel();

    public:
        void Write(Buffer* pBuffer);

    private:
        void DoAction(Buffer* packet);

    private:
        void OnRead(Buffer* pBuffer) throw();
        void OnReadFail(const char* what) throw();

    private:
        IReadHandler* m_readCallback;
        
        pReader m_readStr;
        pWriter m_writeStr;
        pChannelCore m_channel;
        CActionsQueuer<Buffer> m_packagesCache;
    };

              
    //////////////////////////////////////////////////////////////////////////
    template<class ChannelCore, class Reader, class Writer>
    void rdc_transport::CVirtualChannel<ChannelCore, Reader, Writer>::OnReadFail(const char* what) throw()
    {
        m_readCallback->OnReadFail(what);
    }

    template<class ChannelCore, class Reader, class Writer>
    void rdc_transport::CVirtualChannel<ChannelCore, Reader, Writer>::OnRead(Buffer* pBuffer) throw()
    {
        try
        {
            m_packagesCache.Push(pBuffer);
        }
        catch(std::exception& ex)
        {
            m_readCallback->OnReadFail(ex.what());
        }
    }

    template<class ChannelCore, class Reader, class Writer>
    void rdc_transport::CVirtualChannel<ChannelCore, Reader, Writer>::DoAction(Buffer* packet)
    {
        m_readCallback->OnRead(packet);
    }

    template<class ChannelCore, class Reader, class Writer>
    void rdc_transport::CVirtualChannel<ChannelCore, Reader, Writer>::Write(Buffer* pBuffer)
    {
        m_writeStr->Write(pBuffer);
    }

    template<class ChannelCore, class Reader, class Writer>
    rdc_transport::CVirtualChannel<ChannelCore, Reader, Writer>::CVirtualChannel(
        IReadHandler* readCb, pChannelCore channel, pReader reader, pWriter writer)
        : m_readCallback(readCb)
        , m_readStr(reader)
        , m_writeStr(writer)
        , m_channel(channel)
        , m_packagesCache(this)
    {
        m_readStr->SetReadHandler(this);
        m_writeStr->SetInternalWriter(m_channel.get());
    }

    template<class ChannelCore, class Reader, class Writer>
    rdc_transport::CVirtualChannel<ChannelCore, Reader, Writer>::~CVirtualChannel()
    {
        // Check if template classes fit to requirements.
        typedef VcRequirements<ChannelCore, Reader, Writer> Y;
        assert(Y::CheckRequerements());
    }
}